<?php

namespace App\Http\Requests;

use App\Models\DrugDictionary;
use App\Models\Patient;
use Illuminate\Database\Query\Builder;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
use App\Project\Scene\MedicalScene;

class PastHistoryStoreRequest extends FormRequest
{
    /**
     * @var \App\Project\FieldTranslation\FieldTranslation
     */
    protected $field;

    protected $drug;

    protected $scene;

    public function __construct(MedicalScene $scene)
    {
        parent::__construct();
        $this->scene = $scene;
    }

    /**
     * Prepare the data for validation.
     * @return void
     */
    protected function prepareForValidation()
    {
        $this->field = app('field')->get('past');
        $this->drug = app('field')->get('drug');
    }

    public function withValidator(Validator $validator)
    {
        $validator->after(function (Validator $validator) {

            if ($validator->messages()->isEmpty() && \Auth::user()->hospital_id) {
                //  校验 提交的药物是否存在于数据库中

                if ($this->scene->isWestern()) {
                    //西医
                    $this->ifExistInDb($validator);
                } else {
                    //中医
                    $this->chineseIfExistInDb($validator);
                }


            }
        });
    }

    /**
     * 校验 提交的药物是否存在于数据库中
     * @param \Illuminate\Validation\Validator $validator
     */
    public function chineseIfExistInDb(Validator $validator)
    {
        $input = collect($this->input());

        //  获取提交上来的所有药物
        $drugs = $input->pluck('drugs.name')->filter(function ($drug) {
            return !empty($drug);
        })->flatten();

        $drugs = $drugs->merge($input->pluck('extra.drugs.name')->filter(function ($drug) {
            return !empty($drug);
        })->flatten());

        if (!$drugs->count())
            return;  //  无药物提交，直接通过

        //  查询提交的药物是否保存在数据库中
        $drugsInDb = DrugDictionary::select('name')->whereIn('name', $drugs->unique()->toArray())->get();

        //  查询的结果 和 提交的数据 取差集
        $diff = $drugs->diff(Arr::flatten($drugsInDb->toArray()));

        //  无差集代表 提交的药物 全部存在
        if (!$diff->count())
            return;

        $validator->errors()->add('examination', '现服用药物 添加了数据库中不存在的药物！');

        //  遍历输入数据，提交的药物中，存在 $diff 中的药物的 需要返回错误提示
        //        $input->filter(function ($drugs, $key) use ($diff) {
        //            if (is_array($drugs) && array_key_exists('drugs', $drugs) && !empty($drugs['drugs'])) {
        //
        //                return $diff->intersect($drugs['drugs'])->count() ? true : false;
        //            }
        //
        //            return false;
        //        })->keys()->map(function ($key) use ($validator) {
        //            $validator->errors()->add('examination.' . $key, '现服用药物 添加了数据库中不存在的药物！');
        //        });
    }

    /**
     * 校验 提交的药物是否存在于数据库中
     * @param \Illuminate\Validation\Validator $validator
     */
    public function ifExistInDb(Validator $validator)
    {
        $input = collect($this->input());

        //  获取提交上来的所有药物
        $drugs = $input->pluck('drugs')->filter(function ($drug) {
            return !empty($drug);
        })->flatten();

        $drugs = $drugs->merge($input->pluck('extra.drugs')->filter(function ($drug) {
            return !empty($drug);
        })->flatten());

        if (!$drugs->count())
            return;  //  无药物提交，直接通过

        //  查询提交的药物是否保存在数据库中
        $drugsInDb = DrugDictionary::select('name')->whereIn('name', $drugs->unique()->toArray())->get();

        //  查询的结果 和 提交的数据 取差集
        $diff = $drugs->diff(Arr::flatten($drugsInDb->toArray()));

        //  无差集代表 提交的药物 全部存在
        if (!$diff->count())
            return;

        $validator->errors()->add('examination', '现服用药物 添加了数据库中不存在的药物！');

        //  遍历输入数据，提交的药物中，存在 $diff 中的药物的 需要返回错误提示
        //        $input->filter(function ($drugs, $key) use ($diff) {
        //            if (is_array($drugs) && array_key_exists('drugs', $drugs) && !empty($drugs['drugs'])) {
        //
        //                return $diff->intersect($drugs['drugs'])->count() ? true : false;
        //            }
        //
        //            return false;
        //        })->keys()->map(function ($key) use ($validator) {
        //            $validator->errors()->add('examination.' . $key, '现服用药物 添加了数据库中不存在的药物！');
        //        });
    }

    /**
     * Determine if the user is authorized to make this request.
     * @return bool
     */
    public function authorize()
    {
        return \Auth::check();
    }

    /**
     * Get the validation rules that apply to the request.
     * @return array
     */
    public function rules()
    {
        return [
            'record_id' => [
                'required', 'integer', 'min:1',
                Rule::exists('st_medical_records', 'id')->where(function (Builder $query) {
                    $query->whereNull('deleted_at');
                    $user = \Auth::user();
                    if ($user instanceof Patient) {
                        $query->where(['patient_id' => $user->id]);
                    } elseif (!$user->isDoctorsAdmin() && !$user->isAdmin()) {
                        $query->where(['doctor_id' => $user->id]);
                    }
                }),
            ],
            'fat.time' => 'nullable|date|before_or_equal:now',
            'fat.weight' => 'nullable|numeric|min:0|max:999|decimal:1',
            'fat.duration' => 'nullable|date|after_or_equal:fat.time|before_or_equal:now',
            'fat.drugs' => 'nullable|array',
            'hypertension.time' => 'nullable|date|before_or_equal:now',
            'hypertension.drugs' => 'nullable|array',
            'coronary_disease.time' => 'nullable|date|before_or_equal:now',
            'coronary_disease.stenting' => 'nullable|array',
            'coronary_disease.stenting.*' => 'nullable|date|before_or_equal:now',
            'coronary_disease.cabg' => 'nullable|date|before_or_equal:now',
            'coronary_disease.drugs' => 'nullable|array',
            'hyperuricemia.time' => 'nullable|date|before_or_equal:now',
            'hyperuricemia.drugs' => 'nullable|array',
            'hyperlipidemia.time' => 'nullable|date|before_or_equal:now',
            'hyperlipidemia.drugs' => 'nullable|array',
            'fatty_liver.time' => 'nullable|date|before_or_equal:now',
            'fatty_liver.drugs' => 'nullable|array',
            'hepatitis.type' => 'nullable|integer|between:1,5',
            'hepatitis.duration' => 'nullable|date|before_or_equal:now',
            'hepatitis.drugs' => 'nullable|array',
            'pancreatitis.acute' => 'nullable|date|before_or_equal:now',
            'pancreatitis.chronic' => 'nullable|date|before_or_equal:now',
            'pancreatitis.drugs' => 'nullable|array',
            'pancreatic_operation.time' => 'nullable|date|before_or_equal:now',
            'pancreatic_operation.operation' => 'nullable|string|max:20',
            'renal.time' => 'nullable|date|before_or_equal:now',
            'renal.drugs' => 'nullable|array',
            'cerebral_infarction.time' => 'nullable|date|before_or_equal:now',
            'cerebral_infarction.drugs' => 'nullable|array',
            'transfusion.times' => 'nullable|integer|min:0|max:999',
            'irritability.items' => 'nullable|max:50',
            'special_irritability.items' => 'nullable',
            'adverse_drug_reaction.items' => 'nullable',
            'adverse_drug_reaction.symptoms' => 'nullable',
            'thyroid.type' => 'nullable|in:1,2',
            'thyroid.time' => 'nullable|date|before_or_equal:now',
            'thyroid.drugs' => 'nullable|array',
            'trauma.time' => 'nullable|date|before_or_equal:now',
            'trauma.drugs' => 'nullable|array',
            'contagion.time' => 'nullable|date|before_or_equal:now',
            'contagion.drugs' => 'nullable|array',
            'other.time' => 'nullable|date|before_or_equal:now',
            'extra.*.name' => 'required_with:extra.*|nullable|distinct',
            'extra.*.time' => 'nullable|date|before_or_equal:now',
            'extra.*.remark' => 'nullable|max:50',
            'extra.*.drugs' => 'nullable|array',
            'other.drugs' => 'nullable|array',
            'pcos.time' => 'nullable|date|before_or_equal:now',
            'pcos.drugs' => 'nullable|array',
            'operation.*.time' => 'nullable|date_format:Y-m-d|before_or_equal:now',
            'operation.*.name' => 'nullable|required_with:operation.*.time|max:20',

            'medication' => 'nullable|array',
            'medication.*.name' => [
                'required_with:medication.*',
                Rule::exists('st_drug_dictionary', 'name')->where(function (Builder $query) {
                    $query->whereNull('deleted_at');
                }),
            ],
            'medication.*.dosage.type' => 'in:' . implode(',', $this->drug['dosage'][2]['type']),
            'medication.*.dosage.value' => 'numeric|max:999|decimal:2',
            'medication.*.dosage.values' => 'array|min:1|max:4',
            'medication.*.dosage.values.morning' => 'numeric|decimal:2|max:999',
            'medication.*.dosage.values.atnoon' => 'numeric|decimal:2|max:999',
            'medication.*.dosage.values.evening' => 'numeric|decimal:2|max:999',
            'medication.*.dosage.values.before_sleep' => 'numeric|decimal:2|max:999',
            'medication.*.dosage.unit' => 'in:' . implode(',', $this->drug['dosage'][2]['unit']),
            'medication.*.start_time' => 'date',
            'medication.*.end_time' => 'date|after_or_equal:medication.*.start_time',
            'medication.*.usage' => 'in:' . implode(',', $this->drug['usage'][2]),
            'medication.*.frequency' => 'in:' . implode(',', $this->drug['frequency'][2]),
            'medication.*.remark' => 'string|max:50',

            'remark' => 'nullable|string|max:255',
        ];
    }

    public function messages()
    {
        return [
            'record_id.required' => '缺少病历ID',
            'record_id.integer' => '病历ID 必须是整数。',
            'record_id.min' => '病历ID 必须大于等于 :min。',
            'record_id.exists' => '病历 不存在或无修改该病历的权限。',
            '*.time.date' => '无效的时间值。',
            '*.time.before_or_equal' => '无效的时间值，时间值不能早于现在。',

            'fat.weight' => '肥胖史的体重值必须是一个数字。',
            'fat.min' => '肥胖史的体重值必须 介于 :min 到 :max 之间。',
            'fat.decimal' => '肥胖史的体重值 最多保留 1位小数。',

            'medication.*.name.required_with' => '药物名称 不能为空。',
            'medication.*.name.exists' => '药物名称 不存在。',
            'medication.*.dosage.value.required_if' => '每次剂量的剂量值 不能为空。',
            'medication.*.dosage.value.numeric' => '剂量值 必须是一个数字。',
            'medication.*.dosage.value.decimal' => '剂量值 只能保留一位小数。',
            'medication.*.dosage.value.min' => '剂量值 必须大于等于 :min。',
            'medication.*.dosage.value.max' => '剂量值 最大 :max。',
            'medication.*.dosage.values.*.required_if' => '分次剂量的剂量值 不能为空。',
            'medication.*.dosage.values.*.numeric' => '剂量值 必须是一个数字。',
            'medication.*.dosage.values.*.decimal' => '剂量值 只能保留一位小数。',
            'medication.*.dosage.values.*.min' => '剂量值 必须大于等于 :min。',
            'medication.*.dosage.values.*.max' => '剂量值 不能大于 :max。',
            'medication.*.start_time.date' => '无效的时间值。',
            'medication.*.start_time.before_or_equal' => '无效的时间值，时间值不能晚于现在。',
            'medication.*.end_time.date' => '无效的时间值。',
            'medication.*.end_time.after_or_equal' => '结束时间 不能小于 开始时间。',
            'medication.*.end_time.before_or_equal' => '无效的时间值，时间值不能晚于现在。',
            'medication.*.usage.in' => '药品 用法取值错误。',
            'medication.*.frequency.in' => '药品 使用频次取值错误。',

            'remark.max' => '备注最大 :max 个字符',
        ];
    }
}
